package com.cn.web;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.cn.entity.Lottery;
import com.cn.entity.LotteryAttribute;
import com.cn.dto.LotteryDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Author: chenYuncong
 * @Date: 2021/8/25 9:07
 */
@RestController
@Slf4j
public class LotteryController {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private final static String LOTTERY = "LOTTERY::";

    @GetMapping("/lottery/get")
    public Object get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    @GetMapping("/lottery/set")
    public String set(String key, String val) {
        stringRedisTemplate.opsForValue().set(key, val);
        return "OK";
    }


    //新建一个抽奖，
    @PostMapping("/lottery/addLottery")
    public String addLottery(@RequestBody LotteryDTO lotteryDTO) {
//        LotteryDTO lotteryDTO = JSONObject.parseObject(json, LotteryDTO.class);
        Lottery lottery = lotteryDTO.getLottery();
        Assert.notNull(lottery, "lottery 不为空");
        Assert.notNull(lottery.getId(), "lottery.id 不为空");
        Assert.notNull(lottery.getKey(), "lottery.key 不为空");
        List<LotteryAttribute> aList = lotteryDTO.getList();
        Assert.isTrue(!aList.isEmpty(), "aList 元素不能为空");
        aList.forEach(a -> {
            a.setLotteryId(lottery.getId());
            lottery.getAttributeIds().add(a.getId());
        });

        String prefixKey = LOTTERY + lottery.getKey() + "::";
//        lotteryDTO
        //设置抽奖
        Boolean sign = stringRedisTemplate.opsForValue().setIfAbsent(prefixKey, JSON.toJSONString(lottery));
        Assert.isTrue(sign, "这个抽奖key已经存在");

        if (sign) {
            aList.forEach(a -> {
                Assert.notNull(a.getId(), "属性id不为空");
                //抽奖的ids-key
                String key = prefixKey + a.getId() + "::ids";
                String[] strings = a.getIdList().toArray(new String[0]);
                //设置人员集合
                stringRedisTemplate.opsForSet().add(key, strings);

                //设置奖品key
                String prizeKey = prefixKey + a.getId() + "::prizeTotal";
                //设置奖品池数量
                stringRedisTemplate.opsForValue().increment(prizeKey, a.getPrizeTotal());


            });

        }

        return "OK";
    }



    //抽奖接口
    @PostMapping("/lottery/doLottery")
    public List<String> doLottery(String key) {
        Assert.notNull(key, "key 不为空");

        String prefixKey = LOTTERY + key + "::";
        Boolean sign = stringRedisTemplate.hasKey(prefixKey);
        //中奖idsKey，
        String lotteryKey = prefixKey + "lotteryIds";

        List<String> lotteryList = new ArrayList<>();

        if (sign) {
            String obj = stringRedisTemplate.opsForValue().get(prefixKey);
            Lottery lottery = JSON.parseObject(obj, Lottery.class);


            List<Long> ids = lottery.getAttributeIds();

            Map<String, Integer> assMap = this.assignNumber(ids, prefixKey, lottery.getDefaultNumber());



            assMap.forEach((k, v) -> {
                //ids集合key
                String idsKey = k + "::ids";
                //奖品数量key
                String prizeKey = k + "::prizeTotal";
                // v：奖品数量 和 ids数量 取最小值，防止两个值某一变取完
                if(v>0){
                    //奖品池出
                    Long prizeTotal = stringRedisTemplate.opsForValue().decrement(prizeKey, v);
                    if (prizeTotal >= 0) {
                        //随机弹出中奖id
                        List<String> idList = stringRedisTemplate.opsForSet().pop(idsKey, v);

                        lotteryList.addAll(idList);
                        //保存中奖id，方便查询
                        stringRedisTemplate.opsForList().rightPushAll(lotteryKey, idList);
                        log.info("lotteryKey:{},中奖数量：{}，中奖ids：{}", lotteryKey, v, idList);
                    }
                }


            });

        }

        return lotteryList;
    }


    /**
     * 分配集合抽奖人数
     *
     * @param ids
     * @param number
     * @return
     */
    private Map<String, Integer> assignNumber(List<Long> ids, String key, Integer number) {
        Map<String, Integer> map = new HashMap<>();
        LinkedList<Integer> linkedList = new LinkedList<>();
        String prefixKey = key;
        ids.forEach(s -> {
            if (!map.containsKey(s)) {
                map.put(prefixKey + s, 0);
            }
        });
        //奖品总数
        Map<String, Long> totalMap = new HashMap<>();
        //获取到整个活动奖品的总数
        AtomicReference<Long> total = new AtomicReference<>(0L);
        map.forEach((k, v) -> {
            Long num = stringRedisTemplate.opsForValue().increment(k + "::prizeTotal", 0);
            Long idCount = stringRedisTemplate.opsForSet().size(k + "::ids");

            // v：奖品数量 和 ids数量 取最小值，防止两个值某一变取完
            Long numb = num < idCount ? num : idCount;

            totalMap.put(k + "::numTotal", numb);

            total.updateAndGet(v1 -> v1 + numb);
        });

        Long index = number.longValue();
        if (total.get() < number) {
            index = total.get();
        }

        for (int i = 0; i < index; i++) {
            linkedList.add(1);
        }

        while (!linkedList.isEmpty()) {
            map.forEach((k, v) -> {
                if (!linkedList.isEmpty()) {
                    if (map.containsKey(k)) {
//                        Long num = stringRedisTemplate.opsForValue().increment(k + "::prizeTotal", 0);
                        String prizeKey = k + "::numTotal";
                        Long num = totalMap.containsKey(prizeKey) ? totalMap.get(prizeKey) : 0L;
                        if (v < num) {//如果分配至小于自己的总量
                            map.put(k, v + linkedList.removeFirst());
                        }
                    }
                }
            });
        }
        return map;
    }
}
